#include "Scene.h"
//#include "../Gui/Gui_GLOBAL.h"
//
//#include <fstream>
//#include <sstream>
//
//#include <QtXml>
//#include <QtGui>

using namespace std;
using namespace CICore;
using namespace CIGeom;

/// <summary>
/// Constructor
/// </summary>
CIView::Scene::Scene() : geometriesCounter (0), selectionMode (Geometry::FACES_SELECTION), saved (true) {}

/// <summary>
/// Destructor
/// </summary>
CIView::Scene::~Scene() {
    //deleting all geometries
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {
        delete (it->second);
        it++;
    }
}







///<************************************************************************************************************>
///<**************************************** region Utility Methods ********************************************>
///<************************************************************************************************************>

/// <summary>
/// Method called to render the entire scene
/// </summary>
void CIView::Scene::Render() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    //Rendering all geometries
    while (it != geometries.end()) {
        it->second->Render (selectionMode);
        it++;
    }
}

/// <summary>
/// Method called to get the number of
/// geometries that have been added to the scene
/// (used to set the geometries name)
/// </summary>
/// <returns> The number of geometries that have been added to the scene </returns>
unsigned int CIView::Scene::GenerateNewGeometryID() {
    return ++geometriesCounter;
}

/// <summary>
/// Method called to add a geometry in the scene
/// </summary>
/// <param name="g"> A pointer to the geometry to add </param>
/// <returns> A bool to indicate if the the geometry has been added </returns>
bool CIView::Scene::AddGeometry (Geometry *g) {
    if (geometries[g->name.c_str()] != 0)    //If already exists a geometry with the same name
        return false;                        //must return false

    geometries[g->name.c_str()] = g;         //Adds the new geometry

    saved = false;

    return true;
}

/// <summary>
/// Method called to remove a geometry from the scene (NOTE: does not delete the geometry from the heap!!)
/// </summary>
/// <param name="geometryName"> The geometry name </param>
void CIView::Scene::RemoveGeometry (const char *geometryName) {
    saved = false;

    geometries.erase (geometryName);
}

/// <summary>
/// Method called to get a geometry present in the scene
/// </summary>
/// <param name="geometryName"> The geometry name </param>
/// <returns> A pointer to the geometry required </returns>
Geometry* CIView::Scene::GetGeometry (const char *geometryName) {
    return geometries[geometryName];
}

/// <summary>
/// Method called to get a reference to the scene's geometries container
/// </summary>
/// <returns> A reference to the scene's geometries container </returns>
map<const char*, Geometry*>& CIView::Scene::GetGeometries() {
    return geometries;
}

/// <summary>
/// Method called to select all scene's geometries vertices
/// </summary>
void CIView::Scene::SelectAllVertices() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                        //For each geometry
        it->second->SelectAllVertices();                    //Selects all its vertices
        it++;
    }
}

/// <summary>
/// Method called to deselect all scene's geometries vertices
/// </summary>
void CIView::Scene::DeselectAllVertices() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                        //For each geometry
        it->second->DeselectAllVertices();                  //Deselects all its vertices
        it++;
    }
}

/// <summary>
/// invert the current vertices selection status
/// </summary>
void CIView::Scene::InvertVerticesSelection() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                        //For each geometry
        it->second->InvertVerticesSelection();              //Inverts its vertices selection
        it++;
    }
}

/// <summary>
/// Method called to select all scene's geometries faces
/// </summary>
void CIView::Scene::SelectAllFaces() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                        //For each geometry
        it->second->SelectAllFaces();                       //Selects all its faces
        it++;
    }
}

/// <summary>
/// Method called to deselect all scene's geometries faces
/// </summary>
void CIView::Scene::DeselectAllFaces() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                        //For each geometry
        it->second->DeselectAllFaces();                     //Deselects all its faces
        it++;
    }
}

/// <summary>
/// Method called to invert the current faces selection status
/// </summary>
void CIView::Scene::InvertFacesSelection() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                        //For each geometry
        it->second->InvertFacesSelection();                 //Inverts its faces selection
        it++;
    }
}


/// <summary>
/// Method called to hide the selected vertices
/// </summary>
void CIView::Scene::HideSelectedVertices() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                        //For each geometry
        it->second->HideSelectedVertices();                 //hides its selected vertices
        it++;
    }
}

/// <summary>
/// Method called to hide the selected faces
/// </summary>
void CIView::Scene::HideSelectedFaces() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                        //For each geometry
        it->second->HideSelectedFaces();                    //hides its selected faces
        it++;
    }
}

/// <summary>
/// Method called to unhide all primitives
/// </summary>
void CIView::Scene::UnhideAll() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                    //For each geometry
        it->second->UnhideAll();                        //unhides all its primitives
        it++;
    }
}

/// <summary>
/// Method called to know if there are hidden primitives in the scene
/// </summary>
/// <returns> A boolean to indicate if there are primitives hidden </returns>
bool CIView::Scene::HasHiddenPrimitives() {
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                    //If there is at least a geometry with hidden primitives
        if (it->second->hasPrimitivesHidden)
            return true;                                //must return true
        it++;
    }

    return false;
}

/// <summary>
/// Method called to load the names that identify the geometries in the OpenGL names stack
/// </summary>
void CIView::Scene::LoadGeometriesNames () {
    map<const char*, Geometry*>::iterator it = geometries.begin();
    unsigned int geometryName = 0;

    while (it != geometries.end()) {                //For each geometry
        glPushName (geometryName);                  //pushes the geometry name in the OpenGL names stack
        glPushName (0);                             //pushes a fictitious name in the OpenGL names stack
        it->second->LoadNames ();                   //loads the names that identify the geometry in the OpenGL names stack
        glPopName();
        glPopName();

        it++;
        geometryName++;
    }
}

/// <summary>
/// Method called to load the names for the geometries faces in the OpenGL names stack
/// </summary>
void CIView::Scene::LoadFacesNames () {
    map<const char*, Geometry*>::iterator it = geometries.begin();
    unsigned int geometryName = 0;

    while (it != geometries.end()) {                //For each geometry
        glPushName (geometryName);                  //pushes the geometry name in the OpenGL names stack
        glPushName (0);                             //pushes a fictitious name in the OpenGL names stack
        it->second->LoadFacesNames ();              //loads the faces names in the OpenGL names stack
        glPopName();
        glPopName();

        it++;
        geometryName++;
    }
}

/// <summary>
/// Method called to load the names for the geometries vertices in the OpenGL names stack
/// </summary>
void CIView::Scene::LoadVerticesNames () {
    map<const char*, Geometry*>::iterator it = geometries.begin();
    unsigned int geometryName = 0;

    while (it != geometries.end()) {                //For each geometry
        glPushName (geometryName);                  //pushes the geometry name in the OpenGL names stack
        glPushName (0);                             //pushes a fictitious name in the OpenGL names stack
        it->second->LoadVerticesNames ();           //loads the vertices names in the OpenGL names stack
        glPopName();
        glPopName();

        it++;
        geometryName++;
    }
}

///// <summary>
///// Method called to get the total number of faces of the scene geometries
///// </summary>
///// <returns> The total number of faces of the scene geometries </returns>
//unsigned int CIView::Scene::GetNumberOfFaces () {
//    map<const char*, Geometry*>::iterator it = geometries.begin();
//    unsigned int counter = 0;
//
//    while (it != geometries.end()) {                //For each geometry
//        counter += it->second->faces.size();        //adds, to the counter, the number of faces of this geometry
//        it++;
//    }
//    return counter;
//}
//
///// <summary>
///// Method called to get an array that contains pointers to all faces in the scene
///// </summary>
///// <returns> A vector that contains pointers to all faces in the scene </returns>
//std::vector<Face*>* CIView::Scene::GetAllFaces () {
//    map<const char*, Geometry*>::iterator it = geometries.begin();
//    vector<Face*>* faces = new vector<Face*>();
//    Geometry* geometry;
//
//    while (it != geometries.end()) {                    //For each geometry
//        geometry = it->second;
//
//        for (int i = 0; i < geometry->faces.size(); i++)    //for each face
//            faces->push_back (geometry->faces[i]);          //adds the face pointer
//
//        it++;
//    }
//
//    return faces;
//}


/// <summary>
/// Method called to apply a matrix transformation
/// </summary>
/// <param name="m"> The matrix to apply </param>
void CIView::Scene::ApplyMatrix (const Matrix& m) {
    //Applying the transformation to the selected vertices
    map<const char*, Geometry*>::iterator it = geometries.begin();

    while (it != geometries.end()) {                  //For each geometry
        it->second->ApplyMatrix (m);                  //applies the transformation
        it++;
    }

    saved = false;
}


/// <summary>
/// Method called to get the current selection mode enabled
/// </summary>
/// <returns> The current selection mode enabled </returns>
unsigned int CIView::Scene::GetSelectionMode () {
    return selectionMode;
}

/// <summary>
/// Method called to set the current selection mode enabled
/// </summary>
/// <param name="mode"> The selection mode desidered </param>
void CIView::Scene::SetSelectionMode (unsigned int mode) {
    selectionMode = mode;
}

//void spin_ (std::pair<Geometry*, std::ofstream*>& pair) {
//    pair.first->SaveAsCI3D (*pair.second);
//}

///// <summary>
///// Method called to save the current scene
///// </summary>
///// <param name="device"> The file to save the description of the scene </param>
//void CIView::Scene::SaveAsCI3D (const char* filename) {
//    const char* tab = "    ";
//
//    ofstream out;
//    out.open (filename);
//    out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
//        << "<!DOCTYPE Creative_Interaction_3D_version_1.0>\n"
//        << "<CI3D version=\"1.0\">\n";
//
//    out << tab << "<scene>\n"
//        << tab << tab << "<library_geometries>\n";
//
//
//    QProgressDialog dialog ("Please wait...", "Cancel", 0, 100, 0, Qt::FramelessWindowHint);
//    dialog.setStyleSheet (GENERAL_BACKGROUND);//GENERAL_BORDER + "border-radius: 20px;" + GENERAL_BACKGROUND);
//
//    //Creating a QFutureWatcher and connecting signals and slots with the dialog
//    QFutureWatcher<void> futureWatcher;
//    QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset()));
//    QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel()));
//    QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int)));
//    QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int)));
//
//    QVector<pair<Geometry*, ofstream* > > v;
//    pair<Geometry*, ofstream* > pair;
//    pair.second = &out;
//
//    map<const char*, Geometry*>::iterator it = geometries.begin();
//    while (it != geometries.end()) {                        //For each geometry
//        pair.first = it->second;
//        v.push_back (pair);
//        it++;
//    }
//
//    //Starting the computation
//    futureWatcher.setFuture(QtConcurrent::map(v, spin_));
//
//    //Displaying the dialog and starting the event loop
//    dialog.exec();
//
//    //Waiting for the saving completion
//    futureWatcher.waitForFinished();
//
//
//
//    out << tab << tab << "</library_geometries>\n"
//        << tab << "</scene>\n";
//
//
//    out << "</CI3D>\n";
//    saved = true;
//}
//
///// <summary>
///// Method called to load a saved scene
///// </summary>
///// <param name="device"> The file to load the description of the scene </param>
//bool CIView::Scene::LoadFromCI3D (QIODevice* device) {
//    QString errorStr;
//    int errorLine;
//    int errorColumn;
//
//    QDomDocument doc;
//
//    if (!doc.setContent(device, true, &errorStr, &errorLine, &errorColumn)) {
//        QMessageBox::information (0, "CI3D", QString ("Parse error at line %1, column %2:\n%3")
//                                 .arg(errorLine)
//                                 .arg(errorColumn)
//                                 .arg(errorStr));
//        return false;
//    }
//
//    QDomElement root = doc.documentElement();
//
//    if (root.tagName() != "CI3D") {
//        QMessageBox::information (0, "CI3D", "The file is not a CI3D file.");
//        return false;
//    }
//    else if (root.hasAttribute ("version") && root.attribute ("version") != "1.0") {
//        QMessageBox::information (0, "CI3D", "The file is not a CI3D version 1.0 file.");
//        return false;
//    }
//
//
//
//    saved = true;
//}
/***************************************************************************************************************
******************************************* endregion Utility Methods ******************************************
****************************************************************************************************************/
